﻿using ThomasGIS.Coordinates;
using ThomasGIS.Geometries;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ThomasGIS.Vector;

namespace ThomasGIS.Projections
{
    public static class ProjectionProcessor
    {
        public static bool DeProject(IShapefile shapefile)
        {
            CoordinateBase shpOriCoorSys = shapefile.GetCoordinateRef();
            // 如果默认坐标系为空，则自动生成一个WGS84坐标系统
            if (shpOriCoorSys == null || shpOriCoorSys.GetCoordinateType() == CoordinateType.Unknown || shpOriCoorSys.GetCoordinateType() == CoordinateType.Geographic)
            {
                throw new Exception("未知的投影坐标系或地理坐标系，无法执行投影反算！");
            }

            ProjectedCoordinate nowCoordinate = shpOriCoorSys as ProjectedCoordinate;
            GeographicCoordinate baseCoordinate = nowCoordinate.GeoCoordinate;
            IProjection nowProjection = ProjectionGenerator.GenerateProjection(shpOriCoorSys);
            // 获取基准椭球体的参数
            // 长半轴
            double a = baseCoordinate.Datum.Spheroid.SemiMajorAxis;
            // 扁率
            double flatten = baseCoordinate.Datum.Spheroid.InverseFlattening;

            // 并行反算每个Geometry
            Parallel.For(0, shapefile.GetFeatureNumber(), i =>
            {
                IGeometryBase nowGeometry = shapefile.GetFeature(i);
                switch (nowGeometry.GetFeatureType())
                {
                    case 1:
                        Point nowPoint = nowGeometry as Point;
                        nowProjection.Backward(nowPoint, a, flatten);
                        shapefile.SetFeature(i, nowPoint);
                        break;
                    case 3:
                        Polyline nowLine = nowGeometry as Polyline;
                        for (int j = 0; j < nowLine.PointNumber; j++)
                        {
                            nowProjection.Backward(nowLine.PointList[j], a, flatten);
                        }
                        shapefile.SetFeature(i, nowLine);
                        break;
                    case 5:
                        Polygon nowGon = nowGeometry as Polygon;
                        for (int j = 0; j < nowGon.PointNumber; j++)
                        {
                            nowProjection.Backward(nowGon.PointList[j], a, flatten);
                        }
                        shapefile.SetFeature(i, nowGon);
                        break;
                    default:
                        throw new Exception("无法识别的类型！");
                }
            });

            // 还原为地理坐标
            shapefile.SetCoordinateRef(baseCoordinate);

            return true;
        }

        public static bool DoProject(IShapefile shapefile, IProjection towardProjection)
        {
            CoordinateBase shpOriCoorSys = shapefile.GetCoordinateRef();

            // 如果默认坐标系为空，则自动生成一个WGS84坐标系统
            if (shpOriCoorSys == null || shpOriCoorSys.GetCoordinateType() == CoordinateType.Unknown)
            {
                shpOriCoorSys = CoordinateGenerator.CRSParseFromEPSG(4326);
            }
            // 如果当前已经是投影坐标系，则需要先把当前投影解除
            else if (shpOriCoorSys.GetCoordinateType() == CoordinateType.Projected)
            {
                IProjection nowProjection = ProjectionGenerator.GenerateProjection(shpOriCoorSys);
                DeProject(shapefile);
                shpOriCoorSys = shapefile.GetCoordinateRef();
            }

            // 开始进行投影运算
            GeographicCoordinate baseCoordinate = shpOriCoorSys as GeographicCoordinate;

            // 获取基准椭球体的参数
            // 长半轴
            double a = baseCoordinate.Datum.Spheroid.SemiMajorAxis;
            // 扁率
            double flatten = baseCoordinate.Datum.Spheroid.InverseFlattening;

            // 并行投影每个Geometry
            Parallel.For(0, shapefile.GetFeatureNumber(), i =>
            {
                IGeometryBase nowGeometry = shapefile.GetFeature(i);
                switch (nowGeometry.GetFeatureType())
                {
                    case 1:
                        Point nowPoint = nowGeometry as Point;
                        towardProjection.Toward(nowPoint, a, flatten);
                        shapefile.SetFeature(i, nowPoint);
                        break;
                    case 3:
                        Polyline nowLine = nowGeometry as Polyline;
                        for (int j = 0; j < nowLine.PointNumber; j++)
                        {
                            towardProjection.Toward(nowLine.PointList[j], a, flatten);
                        }
                        shapefile.SetFeature(i, nowLine);
                        break;
                    case 5:
                        Polygon nowGon = nowGeometry as Polygon;
                        for (int j = 0; j < nowGon.PointNumber; j++)
                        {
                            towardProjection.Toward(nowGon.PointList[j], a, flatten);
                        }
                        shapefile.SetFeature(i, nowGon);
                        break;
                    default:
                        throw new Exception("无法识别的类型！");
                }
            });

            // 投影为投影坐标
            ProjectedCoordinate projectedCoordinate = new ProjectedCoordinate(baseCoordinate, towardProjection.GetProjectionName());
            Dictionary<string, double> writtenParameters = towardProjection.GetWrittenParameters();
            projectedCoordinate.Projection = towardProjection.GetProjectionType();
            foreach (string key in writtenParameters.Keys)
            {
                projectedCoordinate.Parameters.Add(key, writtenParameters[key]);
            }
            shapefile.SetCoordinateRef(projectedCoordinate);

            return true;
        }

        public static bool DoProject(ICollection<GeometryBase> featureList, IProjection towardProjection, GeographicCoordinate baseCoordinate = null)
        {
            if (baseCoordinate == null)
            {
                baseCoordinate = CoordinateGenerator.CRSParseFromEPSG(4326) as GeographicCoordinate;
            }

            // 获取基准椭球体的参数
            // 长半轴
            double a = baseCoordinate.Datum.Spheroid.SemiMajorAxis;
            // 扁率
            double flatten = baseCoordinate.Datum.Spheroid.InverseFlattening;

            Parallel.ForEach(featureList, oneFeature =>
            {
                int type = oneFeature.GetFeatureType();
                switch (type)
                {
                    case 1:
                        towardProjection.Toward((Point)oneFeature, a, flatten);
                        break;
                    case 3:
                        for (int j = 0; j < ((Polyline)oneFeature).PointNumber; j++)
                        {
                            towardProjection.Toward(((Polyline)oneFeature).PointList[j], a, flatten);
                        }
                        break;
                    case 5:
                        for (int j = 0; j < ((Polygon)oneFeature).PointNumber; j++)
                        {
                            towardProjection.Toward(((Polygon)oneFeature).PointList[j], a, flatten);
                        }
                        break;
                    default:
                        throw new Exception("无法识别的类型！");

                }
            });

            return true;
        }

        public static bool DoGeoTransform(IShapefile shapefile, ITransform towardTransform)
        {
            CoordinateBase nowCoordinate = shapefile.GetCoordinateRef();
            if (nowCoordinate.GetCoordinateType() == CoordinateType.Projected || nowCoordinate.GetCoordinateType() == CoordinateType.Unknown)
            {
                throw new Exception("该功能仅适用于地理坐标间的转换！");
            }

            Parallel.For(0, shapefile.GetFeatureNumber(), i =>
            {
                IGeometryBase nowGeometry = shapefile.GetFeature(i);
                switch (nowGeometry.GetFeatureType())
                {
                    case 1:
                        Point nowPoint = nowGeometry as Point;
                        towardTransform.Toward(nowPoint);
                        shapefile.SetFeature(i, nowPoint);
                        break;
                    case 3:
                        Polyline nowLine = nowGeometry as Polyline;
                        for (int j = 0; j < nowLine.PointNumber; j++)
                        {
                            towardTransform.Toward(nowLine.PointList[j]);
                        }
                        shapefile.SetFeature(i, nowLine);
                        break;
                    case 5:
                        Polygon nowGon = nowGeometry as Polygon;
                        for (int j = 0; j < nowGon.PointNumber; j++)
                        {
                            towardTransform.Toward(nowGon.PointList[j]);
                        }
                        shapefile.SetFeature(i, nowGon);
                        break;
                    default:
                        throw new Exception("无法识别的类型！");
                }
            });

            return true;
        }

        public static bool DeGeoTransform(IShapefile shapefile, ITransform nowTransform)
        {
            CoordinateBase nowCoordinate = shapefile.GetCoordinateRef();
            if (nowCoordinate.GetCoordinateType() == CoordinateType.Projected || nowCoordinate.GetCoordinateType() == CoordinateType.Unknown)
            {
                throw new Exception("该功能仅适用于地理坐标间的转换！");
            }

            // 并行反算每个Geometry
            Parallel.For(0, shapefile.GetFeatureNumber(), i =>
            {
                IGeometryBase nowGeometry = shapefile.GetFeature(i);
                switch (nowGeometry.GetFeatureType())
                {
                    case 1:
                        Point nowPoint = nowGeometry as Point;
                        nowTransform.Backward(nowPoint);
                        shapefile.SetFeature(i, nowPoint);
                        break;
                    case 3:
                        Polyline nowLine = nowGeometry as Polyline;
                        for (int j = 0; j < nowLine.PointNumber; j++)
                        {
                            nowTransform.Backward(nowLine.PointList[j]);
                        }
                        shapefile.SetFeature(i, nowLine);
                        break;
                    case 5:
                        Polygon nowGon = nowGeometry as Polygon;
                        for (int j = 0; j < nowGon.PointNumber; j++)
                        {
                            nowTransform.Backward(nowGon.PointList[j]);
                        }
                        shapefile.SetFeature(i, nowGon);
                        break;
                    default:
                        throw new Exception("无法识别的类型！");
                }
            });

            return true;
        }

        public static bool DoGeoTransform(Point point, ITransform towardTransform)
        {
            towardTransform.Toward(point);
            return true;
        }

        public static bool DeGeoTransform(Point point, ITransform nowTransform)
        {
            nowTransform.Backward(point);
            return true;
        }

        public static bool DoGeoTransform(ICollection<GeometryBase> featureList, ITransform towardTransform)
        {

            Parallel.ForEach(featureList, oneFeature =>
            {
                int type = oneFeature.GetFeatureType();
                switch (type)
                {
                    case 1:
                        towardTransform.Toward((Point)oneFeature);
                        break;
                    case 3:
                        for (int j = 0; j < ((Polyline)oneFeature).PointNumber; j++)
                        {
                            towardTransform.Toward(((Polyline)oneFeature).PointList[j]);
                        }
                        break;
                    case 5:
                        for (int j = 0; j < ((Polygon)oneFeature).PointNumber; j++)
                        {
                            towardTransform.Toward(((Polygon)oneFeature).PointList[j]);
                        }
                        break;
                    default:
                        throw new Exception("无法识别的类型！");

                }
            });

            return true;
        }

        public static bool DeGeoTransform(ICollection<GeometryBase> featureList, ITransform nowTransform)
        {
            Parallel.ForEach(featureList, oneFeature =>
            {
                int type = oneFeature.GetFeatureType();
                switch (type)
                {
                    case 1:
                        nowTransform.Toward((Point)oneFeature);
                        break;
                    case 3:
                        for (int j = 0; j < ((Polyline)oneFeature).PointNumber; j++)
                        {
                            nowTransform.Toward(((Polyline)oneFeature).PointList[j]);
                        }
                        break;
                    case 5:
                        for (int j = 0; j < ((Polygon)oneFeature).PointNumber; j++)
                        {
                            nowTransform.Toward(((Polygon)oneFeature).PointList[j]);
                        }
                        break;
                    default:
                        throw new Exception("无法识别的类型！");

                }
            });

            return true;
        }
    }
}
  